﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using DeepEarth.Core;
using DeepEarth.Map.Core;
using DeepEarth.Map.Core.Extension;
using DeepEarth.Toolkit.Geometry;
using DeepEarth.Toolkit.Geometry.Data;

namespace DeepEarth.Toolkit.Drawing
{
    public abstract class PointDrawProcessor : BaseDrawProcessor
    {
        const double PixelRadius = 5.0;

        public PointDrawProcessor(UIElement interactionSurface, Panel drawingSurface, Func<Location, Point> locationToViewportPoint, Func<Point, Location> viewportPointToLocation)
            : base(interactionSurface, drawingSurface, locationToViewportPoint, viewportPointToLocation)
        {
            PositionOrigin = PositionOrigin.BottomCenter;
        }

        public FrameworkElement RenderedElement { get; set; }

        public PositionOrigin PositionOrigin { get; set; }

        IDisposable clickSubscription;

        IDisposable rightClickSubscription;
        public event EventHandler<DrawProcessorMouseEventArgs> RightClick;

        public void BeginDraw()
        {
            interactionSurface.MouseMove += InteractionSurface_MouseMove;
            drawingSurface.Children.Add(RenderedElement);

            var click = interactionSurface.GetMouseClick();
            clickSubscription = click.Subscribe(c => { c.EventArgs.Handled = false; AddPoint(c.EventArgs); });

            var rightClick = interactionSurface.GetMouseRightButtonDown();
            rightClickSubscription = rightClick.Subscribe(rc => { rc.EventArgs.Handled = true; OnRightClick(rc.EventArgs); });
        }

        public void EndDraw()
        {
            interactionSurface.MouseMove -= InteractionSurface_MouseMove;
            clickSubscription.Dispose();
            rightClickSubscription.Dispose();

            this.drawingSurface.Children.Remove(RenderedElement);
            lastPoint = null;
        }

        public void Clear()
        {
            EndDraw();
        }

        void AddPoint(MouseButtonEventArgs e)
        {
            Point mousePoint = e.GetPosition(interactionSurface);
            if (MouseOverLast(mousePoint))
            {
                OnDrawComplete(null);
                e.Handled = true;
            }
            else
            {
                Location l = base.viewportPointToLocation(mousePoint);
                DrawComplete(l);
            }
        }

        protected virtual void DrawComplete(Location l)
        {
                PointGeometry pointGeometry = new PointGeometry();
                pointGeometry.Position = l;

                OnDrawComplete(pointGeometry);
        }

        Point? lastPoint;
        bool MouseOverLast(Point currentMousePosition)
        {
            try
            {
                return lastPoint != null
                        ? InLocationRadius(lastPoint.Value, currentMousePosition)
                        : false;
            }
            finally
            {
                lastPoint = currentMousePosition;
            }
        }

        bool InLocationRadius(Point locationPoint, Point viewportPoint)
        {
            double difX = viewportPoint.X - locationPoint.X;
            double difY = viewportPoint.Y - locationPoint.Y;

            return Math.Sqrt(Math.Pow(difX, 2.0) + Math.Pow(difY, 2.0)) <= PixelRadius;
        }

        void InteractionSurface_MouseMove(object sender, MouseEventArgs e)
        {
            // Don't take advantage of Dynamic Layer Positioning. Do this ourselves.
            Point mousePoint = e.GetPosition(interactionSurface);

            Point offset = new Point(RenderedElement.ActualWidth * PositionOrigin.X, RenderedElement.ActualHeight * PositionOrigin.Y);

            Canvas.SetLeft(RenderedElement, mousePoint.X - offset.X);
            Canvas.SetTop(RenderedElement, mousePoint.Y - offset.Y);
        }

        void OnRightClick(MouseButtonEventArgs e)
        {
            Point mousePoint = e.GetPosition(interactionSurface);
            Location l = viewportPointToLocation(mousePoint);

            if (RightClick != null)
            {
                RightClick(this, new DrawProcessorMouseEventArgs(mousePoint, l));
            }
        }

    }
}
